Achieving Portability and Reproducibility in R with renv

Introduction

“renv”: To create a portable, reproducible R development environment, use R tools and practices that ensure consistency across machines.

Benefits of renv’s Project-Specific Library:

  • Reproducibility: Each project can maintain its own package versions, ensuring consistent code execution across different machines over time, preventing issues from package updates.

  • Isolation: Projects remain independent, avoiding conflicts between package versions.

  • Portability: The renv.lock file can be shared, enabling others to recreate your environment with renv::restore().

  • Controlled Environments: Full control over installed packages and versions prevents unnecessary dependencies from the global environment.

Basic Steps:

  1. Initialize a project with renv: Start by initializing renv
  2. Add analysis code: Place your analysis code or app script in the folder, e.g.: app.R or analysis.qmd.
  3. Capture the environment: Use renv::snapshot to capture the current package environment.
  4. Reproduce the environment on another machine: Use the renv.lock file to reproduce your environment on a different machine.

1. Initialize a project with renv

RStudio > File > New Project > Name the project > Select path > Select “Use renv with this project” > Create Project.

If you select the “Use renv with this project” option while creating a new RStudio project, you do not need to run renv::init() manually.

# install.packages("renv")
renv::init()  # Need to initialize only once for a project.
The following package(s) are missing their DESCRIPTION files:
- dplyr [D:/Extra projects/working_of_renv/renv/library/windows/R-4.4/x86_64-w64-mingw32/dplyr]
These may be left over from a prior, failed installation attempt.
Consider removing or reinstalling these packages.

2. Add analysis code

# install.packages("stringr")

library(stringr)

# Example function: Use str_length to get the length of a string
 str_length("Hello, World!")
[1] 13
# Option 2: Separate Files: Write code to a separate file for larger projects or better organization, you might want to separate the setup code (`setup.R`) from the application code (`app.R`, `script.R`, `analysis.qmd`).

3. Capture the environment

# Option 1: Take a snapshot manually
renv::snapshot()
- The lockfile is already up to date.
# Option  2: Automatically snapshot after each change (install, update, or remove packages).
options(renv.config.auto.snapshot = TRUE) # For all projects (global)

4. Reproduce the environment on another machine

Used to restore a project’s dependencies from a lockfile, as previously generated by snapshot().

# Run in a fresh R session
# renv::restore()

FAQs

Q) What are the basic files that get created once “renv” is initialized?

It creates 3 files as mentioned below:

  • renv/library: library that contains all packages currently used by your project

  • renv.lock: records metadata about every package so that it can be re-installed on a new machine. Generated once you install or snapshot your packages.

  • .Rprofile: This file is run automatically every time you start R (in that project), and renv uses it to configure your R session to use the project library

Q) What packages are installed when a new “renv” project is created?

  • When you initialize a project with renv, it sets up a local environment where only the base R packages (those that come with R by default) are available at first, meaning you will need to install packages separately for each project.

  • This creates a local library specific to your project and saves the package versions in a “renv.lock file”. Other users can use this file to recreate the same environment.

Q) Doesn’t this take more space as library will be installed for each project separately?

Not really, as renv offers some features to avoid having to install packages multiple times, as mentioned below:

  1. Global Cache: renv uses a global cache by default. This means that when you install a package for one project, it gets stored in a shared cache on your system. If another project requires the same version of that package, renv will symlink the package from the global cache to the new project’s library, saving time and disk space. The use of a global cache helps minimize redundant installations.

  2. Snapshot and Restore: You don’t need to install packages manually for every project. You can snapshot your current environment (renv::snapshot()), and when you share your project, others (or you on a different machine) can use renv::restore() to automatically install all required packages based on the lockfile.

# To check the cache location:
renv::paths$cache()
[1] "C:\\Users\\hp\\AppData\\Local/R/cache/R/renv/cache/v5/windows/R-4.4/x86_64-w64-mingw32"
# To check where the libraries have got installed:
.libPaths()
[1] "D:/Extra projects/working_of_renv/renv/library/windows/R-4.4/x86_64-w64-mingw32"           
[2] "C:/Users/hp/AppData/Local/R/cache/R/renv/sandbox/windows/R-4.4/x86_64-w64-mingw32/e0da0d43"
lapply(.libPaths(), list.files)
[[1]]
 [1] "askpass"        "backports"      "base64enc"      "bench"         
 [5] "bit"            "bit64"          "blob"           "brio"          
 [9] "broom"          "bslib"          "cachem"         "callr"         
[13] "cli"            "colorspace"     "covr"           "cpp11"         
[17] "crayon"         "curl"           "DBI"            "dbplyr"        
[21] "desc"           "diffobj"        "digest"         "dplyr"         
[25] "evaluate"       "fansi"          "farver"         "fastmap"       
[29] "fontawesome"    "fs"             "generics"       "ggplot2"       
[33] "glue"           "gtable"         "highr"          "htmltools"     
[37] "httr"           "isoband"        "jquerylib"      "jsonlite"      
[41] "knitr"          "labeling"       "Lahman"         "lazyeval"      
[45] "lifecycle"      "lobstr"         "magrittr"       "memoise"       
[49] "microbenchmark" "mime"           "munsell"        "nycflights13"  
[53] "openssl"        "pillar"         "pkgbuild"       "pkgconfig"     
[57] "pkgload"        "plogr"          "praise"         "prettyunits"   
[61] "processx"       "profmem"        "ps"             "purrr"         
[65] "R6"             "rappdirs"       "RColorBrewer"   "rematch2"      
[69] "renv"           "rex"            "rlang"          "rmarkdown"     
[73] "RMySQL"         "RPostgreSQL"    "rprojroot"      "RSQLite"       
[77] "sass"           "scales"         "stringi"        "stringr"       
[81] "sys"            "testthat"       "tibble"         "tidyr"         
[85] "tidyselect"     "tinytex"        "utf8"           "vctrs"         
[89] "viridisLite"    "waldo"          "withr"          "xfun"          
[93] "yaml"          

[[2]]
 [1] "base"       "boot"       "class"      "cluster"    "codetools" 
 [6] "compiler"   "datasets"   "foreign"    "graphics"   "grDevices" 
[11] "grid"       "KernSmooth" "lattice"    "MASS"       "Matrix"    
[16] "methods"    "mgcv"       "nlme"       "nnet"       "parallel"  
[21] "rpart"      "spatial"    "splines"    "stats"      "stats4"    
[26] "survival"   "tcltk"      "tools"      "utils"     

Q) How to check the content renv.lock file

Open the file with notepad or to view content in R use below code:

lockfile <- renv::lockfile_read("renv.lock")
# print(lockfile)

Q) How to check any discrepancies between the current installed packages and the snapshot

renv::status()
The following package(s) are missing their DESCRIPTION files:
- dplyr [D:/Extra projects/working_of_renv/renv/library/windows/R-4.4/x86_64-w64-mingw32/dplyr]
These may be left over from a prior, failed installation attempt.
Consider removing or reinstalling these packages.

No issues found -- the project is in a consistent state.

Q) How to change the working directory back to R’s default workspace

# setwd("~")

Q) How to convert a renv project back to a standard R project

  1. Set Your Working Directory:
    setwd(“path/to/your/project/folder”)

  2. Remove renv:
    unlink(“renv”, recursive = TRUE)
    file.remove(“renv.lock”)

  3. Install Global Packages (if needed):
    install.packages(c(“dplyr”, “ggplot2”)

  4. Remove references to renv code:
    such as: renv::activate(), renv::snapshot(), renv::restore()

Q) How can I set up renv for an existing folder containing my app/script if I forgot to initialize it as a renv project or take a snapshot?

  1. Navigate to the folder containing the script that you need a snapshot of

  2. Initialize renv in the current folder. (Needed only once) This will create “renv.lock” with packages that have been loaded/used in the App or script (example: using library(“dplyr”))

  3. Take a snapshot of the current environment

# setwd("replace_path/to/your/folder")

# renv::init() 

# renv::snapshot() # Run the command manually when needed

Q) Is “renv” a complete solution to reproducibility and portability?

No, while renv helps manage R package versions, it does not handle all aspects of reproducibility (e.g., data, scripts, configuration files).

Limitations and Drawbacks of renv

  1. Package Availability
  • If a required package is not available on CRAN or a configured repository, it cannot be installed.
  • Projects relying on external or non-CRAN packages may face challenges.

Addressing the Limitation Install non-CRAN packages using alternative methods such as:

  • GitHub: Use remotes::install_github("username/repo") to install packages directly from GitHub.

  • Local Installation: install packages from local source files using install.packages("path/to/package.tar.gz", repos = NULL, type = "source").

  1. Environment Differences
  • If the target machine has a different R version or system architecture, there may be compatibility issues.
  • Operating system differences can lead to inconsistencies, especially for packages that rely on system libraries.

Addressing the Limitation
Example store R version: - R.version to capture the current R version, then saving this information in a README file or a separate configuration file within your project.

  1. Manual Management
  • Users must still manage package installations and dependencies outside the renv context (e.g., certain system dependencies).

Addressing the Limitation:
Installing system libraries required by specific R packages, such as libcurl for packages that rely on web requests. For example, on Ubuntu, you might need to run
sudo apt-get install libcurl4-openssl-dev.

  1. Performance Overhead
  • The use of isolated libraries can lead to increased disk space usage and slower performance during package loading in larger projects.